#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
 @Author: Y-aong
 @FileName: main.py
 @DateTime: 2024/5/25 18:11
 @Version : 3.10
 @Description: orderlines_web_plugin
"""
import os
import uuid

from robotremoteserver import RobotRemoteServer
from selenium import webdriver
from selenium.common import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as condition

from logger import logger
from web_exception import ElementHasNoAttribute, ElementTimeoutException, ElementNotFindException


class OrderlinesWebPlugin:
    def __init__(self):
        self.driver = None

    def web_config(
            self,
            execute_path: str = r'C:\\Program Files\\EView\\EView.exe',
            max_size: bool = True,
            height: int = 1920,
            width: int = 1080,
            max_wait_time: float = 30
    ):
        """
        WEB UI配置
        :param execute_path: 执行路径
        :param max_size: 是否最大化
        :param height: 浏览器高度
        :param width: 浏览器宽度
        :param max_wait_time: 等待时间
        :return:
        """
        logger.info('开始配置WEB UI')
        # 浏览器的基础配置
        options = webdriver.ChromeOptions()
        options.binary_location = execute_path  # 指定浏览器位置
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option("useAutomationExtension", 'False')
        options.add_argument('--disable-infobars')
        options.add_argument('--diable-gpu')
        options.add_argument("--start-maximized")  # 添加最大化窗口运作参数
        options.add_experimental_option('detach', True)  # 保持浏览器的打开状态
        # 创建driver实例，并设置隐私等待时间
        self.driver = webdriver.Chrome(options=options)
        self.driver.implicitly_wait(max_wait_time)
        if execute_path.endswith('chrome.exe'):
            if max_size:
                self.driver.maximize_window()
            else:
                self.driver.set_window_size(height, width)
        logger.info('配置WEB UI完成')

    def _get_element(self, selector: str) -> WebElement:
        """
        获取元素
        :param selector:元素选择器
        :return:
        """
        elements = self.driver.find_elements(by=By.XPATH, value=selector)
        for element in elements:
            if not element.is_enabled():
                logger.warning(f'元素{selector}不可用')
                continue
            if not element.is_displayed():
                logger.warning(f'元素{selector}不可见')
                continue
            return element
        raise ElementNotFindException(f'根据元素选择器{selector}没有找到可见元素')

    def get_token(self, token_name: str = None):
        """
        获取浏览器token
        :param token_name:token名称
        :return: token
        """
        if token_name:
            return self.driver.get_cookie(token_name)
        else:
            return self.driver.get_cookies()

    def goto_page(self, url: str):
        """
        前往指定页面
        :param url:url路径
        :return:
        """
        self.driver.get(url)
        logger.info(f'前往页面:{url}成功')

    def input(self, selector: str, text: str, timeout=30):
        """
        输入元素
        :param selector:元素选择器
        :param text: 输入文字
        :param timeout: 超时时间
        :return:
        """
        self.wait_for_element(selector, timeout)
        element = self._get_element(selector)
        element.clear()
        element.send_keys(str(text))
        attribute_value = self.get_element_attribute(selector, 'value')
        if attribute_value != text:
            raise ValueError(f'输入框{selector}输入{text}失败')

        logger.info(f'元素{selector},输入文字{text}成功')

    def click(
            self,
            selector: str,
            double: bool = False,
            validate_selector=None,
            offset_x: int = 0,
            offset_y: int = 0,
            timeout=30
    ):

        """
        点击元素
        :param selector:元素选择器
        :param double: 是否双击
        :param validate_selector: 验证选择器
        :param offset_x: x轴偏移距离
        :param offset_y: y轴偏移距离
        :param timeout: 超时时间
        :return:
        """
        logger.info('开始元素点击')
        self.wait_for_element(selector, timeout)
        element = self._get_element(selector)
        # 点击偏移
        ActionChains(self.driver).move_to_element_with_offset(element, offset_x, offset_y)
        if double:
            ActionChains(self.driver).double_click(element).perform()
            logger.info(f'元素{selector},双击成功')
        else:
            element.click()
            logger.info(f'元素{selector},点击成功')
        if validate_selector:
            self.wait_for_element(validate_selector, timeout)

    def drag_and_drop(self, source_selector: str, target_selector: str, timeout: int = 30):
        """
        拖放元素
        @param source_selector:源元素选择器
        @param target_selector:目标元素选择器
        @param timeout:超时时间
        @return:
        """
        self.wait_for_element(source_selector, timeout)
        self.wait_for_element(target_selector, timeout)

        source_element = self._get_element(source_selector)
        target_element = self._get_element(target_selector)
        ActionChains(self.driver).drag_and_drop(source_element, target_element).perform()

    def keyboard(self, selector: str, key_mapping: list, timeout=30):
        """
        浏览器键盘操作
        @param selector:元素选择器
        @param key_mapping:键位
        @param timeout:超时时间
        @return:
        """
        logger.info(f'输入参数::元素选择器::{selector}, 键位::{key_mapping}')
        self.wait_for_element(selector, timeout)
        element = self._get_element(selector)
        element.send_keys(*key_mapping)

    def get_element_attribute(self, selector: str, attribute: str = 'text', timeout=30):
        """
        获取元素属性
        :param selector:元素选择器
        :param attribute: 元素属性
            目前支持的元素属性为
            text:   获取特定元素渲染后的文本内容。
            value:  它返回与该元素的 DOM 特性或属性关联的数据。
            tag:    获取元素标签名
            其他 WebElement的属性或者WebElement.get_attribute()支持的属性
        :param timeout:超时时间
        :return:
        """
        try:
            self.wait_for_element(selector, timeout)
            element = self._get_element(selector)
            if hasattr(element, attribute):
                attribute_value = getattr(element, attribute)
            else:
                attribute_value = element.get_attribute(attribute)
            logger.info(f'元素{selector}获取{attribute}成功')
            return attribute_value
        except Exception as error:
            raise ElementHasNoAttribute(f'当前元素{selector}, 没有{attribute}属性\n, 异常信息::{error}')

    def get_element_rect(self, selector: str, timeout: int = 30):
        """获取元素位置大小"""
        self.wait_for_element(selector, timeout)
        element = self._get_element(selector)
        return element.rect

    def wait_for_element(self, selector: str, timeout: int = 30):
        """
        等待元素出现
        :param selector: 元素选择器
        :param timeout: 等待时间
        :return:
        """
        try:
            WebDriverWait(self.driver, timeout).until(condition.visibility_of_element_located((By.XPATH, selector)))
            logger.info(f'元素{selector}等待{timeout}元素出现')
        except NoSuchElementException:
            raise ElementTimeoutException(f'元素等待{timeout}还未出现')
        except Exception as e:
            raise Exception(f'等待元素出现异常,异常信息::{e}')

    def execute_script(self, js_code: str):
        """执行js代码"""
        response = self.driver.execute_script(js_code)
        logger.info(f'执行js代码{js_code}成功')
        return response

    def close_page(self, index: int):
        """关闭页面"""
        all_page = self.driver.window_handles
        target_page = all_page[index]
        self.driver.switch_to.window(target_page)
        self.driver.close()
        logger.info(f'关闭页面{index}成功.')

    def save_screenshot(self, image_path: str):
        """保存图片"""
        if not os.path.exists(image_path):
            os.makedirs(image_path)
        image_path = os.path.join(image_path, f'{uuid.uuid4().hex}.jpg')
        self.driver.save_screenshot(image_path)
        logger.info(f'图片保存成功，图片路径::{image_path}')

    def get_elements_num(self, selector: str, timeout=30):
        """
        获取元素数量
        @param selector:元素选择器
        @param timeout:超时时间
        @return:
        """
        logger.info(f'元素选择器::{selector}.')
        self.wait_for_element(selector, timeout)
        return len(self.driver.find_elements(by=By.XPATH, value=selector))

    def forward(self):
        """页面前进"""
        self.driver.forward()
        logger.info(f'页面前进成功.')

    def back(self):
        """页面后退"""
        self.driver.back()
        logger.info(f'页面后退成功.')

    def refresh(self):
        """页面刷新"""
        self.driver.refresh()
        logger.info(f'浏览器刷新成功.')

    def quit(self):
        """退出浏览器"""
        self.driver.quit()
        logger.info(f'退出浏览器成功.')


if __name__ == '__main__':
    RobotRemoteServer(OrderlinesWebPlugin()).activate()
